package com.example.demo.Security;

import com.example.demo.Dao.PersonRepository;
import com.example.demo.Entity.Person;
import com.example.demo.Qo.PersonQo;
import com.example.demo.Utils.CodeUtils;
import com.example.demo.Utils.ConstUtils;
import com.example.demo.Utils.JavaWebTokenUtils;
import com.example.demo.Utils.RedisUtils;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.tomcat.util.bcel.Const;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;


/**
 * JavaWebTokenConfigurer
 *
 * 有点小瑕疵  在认证权限不一致时 返回码暂且不知道如何自定义
 *
 * @blame Android Team
 */
public class JavaWebTokenConfigurer <H extends HttpSecurityBuilder<H>>
    extends AbstractHttpConfigurer<JavaWebTokenConfigurer<H>, H>
{


    @Override
    public void configure(H http) throws Exception {
//        http.addFilterAfter(new UserPasswordFilter(),UsernamePasswordAuthenticationFilter.class);
//        http.addFilterBefore(new JavaWebTokenFilter(),UsernamePasswordAuthenticationFilter.class);
    }


  public   static class UserPasswordFilter extends UsernamePasswordAuthenticationFilter{
        private static final String DEFAULT_CALLBACK_BASE_URI = "/authentication/login";
        private AuthenticationManager authenticationManager;
        private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();
        private boolean continueChainBeforeSuccessfulAuthentication = false;
        private RedisUtils redisUtils;
        public UserPasswordFilter(AuthenticationManager authenticationManager,RedisUtils redisUtils) {
            this.authenticationManager = authenticationManager;
            this.redisUtils = redisUtils;
            super.setFilterProcessesUrl(DEFAULT_CALLBACK_BASE_URI);
        }

      @Override
      public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
          HttpServletRequest request = (HttpServletRequest) req;
          HttpServletResponse response = (HttpServletResponse) res;

          response.setCharacterEncoding("UTF-8");
          response.setContentType("application/json; charset=utf-8");
          if (!requiresAuthentication(request, response)) {
              chain.doFilter(request, response);

              return;
          }
          System.out.println("进入UP过滤器");

          if (logger.isDebugEnabled()) {
              logger.debug("Request is to process authentication");
          }

          Authentication authResult;

          try {
              authResult = attemptAuthentication(request, response);
              if (authResult == null) {
                  response.getWriter().write(CodeUtils.Message.encodeMessageToToString(CodeUtils.Message.builder().code(44033).message("账号密码错误").build()));
                  return;
              }
              sessionStrategy.onAuthentication(authResult, request, response);
          }
          catch (InternalAuthenticationServiceException failed) {
              logger.error(
                      "An internal error occurred while trying to authenticate the user.",
                      failed);
              unsuccessfulAuthentication(request, response, failed);

              return;
          }
          catch (AuthenticationException failed) {
              // Authentication failed
              unsuccessfulAuthentication(request, response, failed);
              return;
          }

          // Authentication success
          if (continueChainBeforeSuccessfulAuthentication) {
              chain.doFilter(request, response);
          }

          successfulAuthentication(request, response, chain, authResult);
      }

      @Override
        public Authentication attemptAuthentication(HttpServletRequest request,
                                                    HttpServletResponse response) {

            // 从request获取到登录的信息
            try {
                String username = request.getParameter("username");
                String password = request.getParameter("password");
                System.out.println(username+"============"+password);
                return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken( username , password));
            } catch (Exception e) {
//                e.printStackTrace();
                return null;
            }
        }
        // 成功验证后调用的方法
        // 如果验证成功，就生成token并返回
        @Override
        protected void successfulAuthentication(HttpServletRequest request,
                                                HttpServletResponse response,
                                                FilterChain chain,
                                                Authentication authResult) throws IOException, ServletException {

            Person jwtUser = (Person) authResult.getPrincipal();
            String token;
            if (redisUtils.hasKey(jwtUser.getUsername()))
            {
                token = redisUtils.get(jwtUser.getUsername()).toString();
            }else {
                 token = JavaWebTokenUtils.generateToken(jwtUser);
                 token = ConstUtils.TokenUtils.TOKEN_PREFIX + token;
                 redisUtils.set(jwtUser.getUsername(),token,60*60*24*10);
            }
            System.out.println("网页token"+token);
            // 按照jwt的规定，最后请求的时候应该是 `Bearer token`
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            response.setHeader(ConstUtils.TokenUtils.TOKEN_HEADERS,token);
            response.getWriter().write(new ObjectMapper().writeValueAsString(CodeUtils.Message.builder().code(20000).message(token).build()));
            response.setHeader("Access-Control-Allow-Origin","*");

//            chain.doFilter(request, response);
        }

      @Override
      protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {

          response.getWriter().write(CodeUtils.Message.encodeMessageToToString(CodeUtils.Message.builder().code(55044).message("服务器发生未知错误")));
      }


    }


    public   static class JavaWebTokenFilter extends BasicAuthenticationFilter {

        private static final String DEFAULT_CALLBACK_BASE_URI = "/authentication/login";
        private RedisUtils redisUtils;

        public JavaWebTokenFilter(AuthenticationManager authenticationManager,RedisUtils redisUtils) {
            super(authenticationManager);
            this.redisUtils = redisUtils;
        }
        @Override
        protected void doFilterInternal (HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {



            String tokenHeader = request.getHeader(ConstUtils.TokenUtils.TOKEN_HEADERS);
            if (tokenHeader == null || !tokenHeader.startsWith(ConstUtils.TokenUtils.TOKEN_PREFIX)) {
                chain.doFilter(request, response);
                return;
            }
            //            如果header中含有 jwt  则 拦截请求 将token 转化为 Authentication   并调用BasicAuthentic$success方法
            SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader));

            super.doFilterInternal(request, response, chain);



        }
        private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader) {
            String token = tokenHeader.replace(ConstUtils.TokenUtils.TOKEN_PREFIX, "");
            String username = JavaWebTokenUtils.getUsernameFromToken(token);
            String role = JavaWebTokenUtils.getUserRoleFromToken(token);

            if (username != null){
                boolean result  = JavaWebTokenUtils.validateToken(token,username,redisUtils);
                System.out.println("验证token："+result);
                if (result){
                    return new UsernamePasswordAuthenticationToken(username, null,
                            Collections.singleton(new SimpleGrantedAuthority(role)));
                }

            }
            return null;
        }

    }
}
